package com.pansky.crm.service.impl;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.pansky.crm.common.mybatisplus.ApiResult;
import com.pansky.crm.config.redis.RedisBean;
import com.pansky.crm.constants.CommonConstant;
import com.pansky.crm.entity.Employee;
import com.pansky.crm.entity.UserLoginLog;
import com.pansky.crm.entity.bo.EmployeeLoginFormDTO;
import com.pansky.crm.entity.vo.EmployeeInfo;
import com.pansky.crm.entity.vo.KaptchaVO;
import com.pansky.crm.enums.JudgeEnum;
import com.pansky.crm.service.EmployeeService;
import com.pansky.crm.service.PrivilegeService;
import com.pansky.crm.service.UserLoginLogService;
import com.pansky.crm.util.AES256Util;
import com.pansky.crm.util.TokenUtil;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author ：zhouzhiyong
 * @date ：Created in 2021/2/25 14:14
 * @description：
 */
@Service
@Slf4j
public class LoginService {

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    private static final String VERIFICATION_CODE_REDIS_PREFIX = "vc_%s";

    @Autowired
    private EmployeeService employeeService;

    @Autowired
    private PrivilegeService privilegeService;

    @Autowired
    private UserLoginLogService userLoginLogService;

    @Autowired
    private RedisBean redisBean;

    public ApiResult<EmployeeInfo> login(EmployeeLoginFormDTO loginForm, HttpServletRequest request) {
//        String redisVerificationCode = redisBean.get(loginForm.getCodeUuid()).toString();
//        //增加删除已使用的验证码方式 频繁登录
//        redisBean.del(loginForm.getCodeUuid());
//        if (!redisVerificationCode.equalsIgnoreCase(loginForm.getCode())) {
//            return ApiResult.failed("验证码无效");
//        }
        String loginName = loginForm.getLoginName();
        String loginPwd = loginForm.getLoginPwd();
        try {
            Subject subject = SecurityUtils.getSubject();
            loginPwd = AES256Util.encode(AES256Util.DEFAULT_SECRET_KEY, loginPwd);
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginName, loginPwd);
            usernamePasswordToken.setRememberMe(true);
            subject.login(usernamePasswordToken);
            Employee employee = new Employee();
            employee.setLoginName(loginName);
            employee.setIsDisabled(0);
            List<EmployeeInfo> employeeInfos = employeeService.findEmployeeInfo(employee);
            EmployeeInfo employeeInfo = null;
            if (CollectionUtils.isNotEmpty(employeeInfos)) {
                employeeInfo = employeeInfos.get(0);
            }
            assert employeeInfo != null;
            String token = TokenUtil.getToken(loginName, employeeInfo.getId().toString(), request.getRemoteAddr());
            redisBean.addKey("LOGIN_TOKEN:" + loginName, token, CommonConstant.EXPIRE_TIME_SECOND, TimeUnit.SECONDS);
            employeeInfo.setXaccessToken(token);
            System.out.println("login subject session : " + subject.getSession().getId());
            //登陆操作日志
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
            UserLoginLog logEntity =
                    UserLoginLog.builder()
                            .userId(employeeInfo.getId())
                            .userName(employeeInfo.getActualName())
                            .remoteIp(request.getRemoteAddr())
                            .remotePort(request.getRemotePort())
                            .remoteBrowser(userAgent.getBrowser().getName())
                            .remoteOs(userAgent.getOperatingSystem().getName())
                            .loginStatus(JudgeEnum.YES.getValue()).build();
            userLoginLogService.save(logEntity);
            return ApiResult.ok(employeeInfo);
        } catch (Exception e) {
            log.error("login fail,", e);
            return ApiResult.failed("登陆失败,用户名或密码错误.");
        }
    }
    public ApiResult<KaptchaVO> verificationCode() {
        KaptchaVO kaptchaVO = new KaptchaVO();
        String uuid = buildVerificationCodeRedisKey(UUID.randomUUID().toString());
        String kaptchaText = defaultKaptcha.createText();

        String base64Code = "";
        BufferedImage image = defaultKaptcha.createImage(kaptchaText);
        ByteArrayOutputStream outputStream = null;
        try {
            outputStream = new ByteArrayOutputStream();
            ImageIO.write(image, "jpg", outputStream);
            base64Code = Base64.encodeBase64String(outputStream.toByteArray());
        } catch (Exception e) {
            log.error("verificationCode exception .{}", e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (Exception e) {
                    log.error("verificationCode outputStream close exception .{}", e);
                }
            }
        }
        kaptchaVO.setUuid(uuid);
        kaptchaVO.setCode("data:image/png;base64," + base64Code);
        redisBean.addKey(uuid, kaptchaText, 60L, TimeUnit.SECONDS);
        return ApiResult.ok(kaptchaVO);
    }

    public ApiResult<EmployeeInfo> getSession(String token) {
        Claims tokenBody = TokenUtil.getTokenBody(token);
        String loginName = tokenBody.getSubject();
        Employee employee = new Employee();
        employee.setLoginName(loginName);
        List<EmployeeInfo> employeeInfos = employeeService.findEmployeeInfo(employee);
        EmployeeInfo employeeInfo = null;
        if (CollectionUtils.isNotEmpty(employeeInfos)) {
            employeeInfo = employeeInfos.get(0);
        }
        assert employeeInfo != null;
        return ApiResult.ok(employeeInfo);
    }

    private String buildVerificationCodeRedisKey(String uuid) {
        return String.format(VERIFICATION_CODE_REDIS_PREFIX, uuid);
    }
}
